package IA;

/**
 * 
 * @author ALTUNTAS
 * @author HUVE
 * 
 */
public class Noeud {

	private int profondeurCourante;
	private int nbFils;
	private Noeud[] listeFils;
	private Coup coupArrivant;
	private IntervalleAlphaBeta iAB;

	private static int[][] plateau;
	private static int profondeurMax;
	private static int joueurInitial;
	private static Coup coupOptimal;
	private static int numeroTour;
	private static Coup coupInitialPremierTour = null;
	private static Coup coupTemporaireDeuxiemeTour = null;

	public Noeud(int profCour, int nbFi, Coup coupArr,
			IntervalleAlphaBeta iAlphBet) {
		this.profondeurCourante = profCour;
		this.nbFils = nbFi;
		this.listeFils = null; // par defaut
		this.coupArrivant = coupArr;
		this.iAB = iAlphBet;
		if (profondeurCourante == 0) {
			numeroTour = 226 - nbFi;
			if (numeroTour == 2) {
				int a = 0, b = 0;
				while (b < 15) {
					while (a < 15) {
						if (plateau[a][b] == -joueurInitial) {
							coupInitialPremierTour = new Coup(a, b);
							a = 14;
							b = 14;
						}
						a++;
					}
					a = 0;
					b++;
				}
			}
		}
		if (numeroTour == 2 && profondeurCourante == 1) {
			coupTemporaireDeuxiemeTour = coupArrivant;
			this.nbFils = 0;
		}
		if (this.coupArrivant != null) {
			plateau[this.coupArrivant.getAbsJouee()][this.coupArrivant
					.getOrdJouee()] = joueurInitial
					* (((this.profondeurCourante % 2) * 2) - 1); // le coup est
																	// joue
		}
		if (profondeurMax == this.profondeurCourante) {
			this.nbFils = 0;
		}
		if (this.nbFils > 0) {
			this.listeFils = new Noeud[this.nbFils];
		}
		int i = 0;
		int oldMaxProvis = -Integer.MAX_VALUE;
		while (i < this.nbFils
				&& this.iAB.getMaxProvisoire() < this.iAB.getBeta()) {
			this.listeFils[i] = new Noeud(this.profondeurCourante + 1,
					this.nbFils - 1, this.getNiemeCoupJouable(i + 1),
					new IntervalleAlphaBeta(-this.iAB.getBeta(), -this.iAB
							.getBeta(), -this.iAB.getMaxProvisoire()));
			oldMaxProvis = this.iAB.getMaxProvisoire();
			this.iAB.setMaxProvisoire(Math.max(this.iAB.getMaxProvisoire(),
					-this.listeFils[i].getIntervalleAlphaBeta()
							.getMaxProvisoire()));
			if ((profondeurCourante == 0)
					&& (oldMaxProvis < this.iAB.getMaxProvisoire())) {
				coupOptimal = this.getNiemeCoupJouable(i + 1);
			}
			listeFils[i] = null;
			i++;
		}
		if (this.nbFils == 0) {
			this.iAB.setMaxProvisoire(Math.max(this.iAB.getMaxProvisoire(),
					this.heuristique()));
		}
		if (this.coupArrivant != null) {
			plateau[this.coupArrivant.getAbsJouee()][this.coupArrivant
					.getOrdJouee()] = 0; // le coup est backtracke
		}
	}

	/** debut des methodes d'heuristique ********************************************/

	private int heuristique() {
		int res = 0;
		int joueurCour = -(joueurInitial * (((this.profondeurCourante % 2) * 2) - 1));
		for (int j = 0; j < 15; j++) {
			for (int i = 0; i < 15; i++) {
				if (plateau[i][j] == joueurInitial) {
					if (joueurInitial == 1) {
						// TODO coeficient : degre d'attaque de l'ia du joueur
						// 1. Jeu normal : 1.0; Defense extreme : 2.0; Defense
						// faible : <1.0
						res += 1.0 * scorePion(joueurInitial, i, j);
					} else {
						// TODO coeficient : degre d'attaque de l'ia du joueur
						// 2. Jeu normal : 1.0; Defense extreme : 2.0; Defense
						// faible : <1.0
						res += 1.0 * scorePion(joueurInitial, i, j);
					}
				}
				if (plateau[i][j] == -joueurInitial) {
					if (joueurInitial == 1) {
						// TODO coeficient : degre de defense de l'ia du joueur
						// 1. Jeu normal : 1.0; Defense extreme : 2.0; Defense
						// faible : <1.0
						res -= 1.0 * scorePion(-joueurInitial, i, j);
					} else {
						// TODO coeficient : degre de defense de l'ia du joueur
						// 2. Jeu normal : 1.0; Defense extreme : 2.0; Defense
						// faible : <1.0
						res -= 1.3 * scorePion(-joueurInitial, i, j);
					}

				}
			}
		}

		return joueurInitial * (res * joueurCour);
	}

	public int scorePion(int joueur, int abs, int ord) {
		int res = 0;

		if (numeroTour == 1 && joueur == joueurInitial) {
			if (abs == 7 && ord == 7 && plateau[abs][ord] == joueur) {
				res = 20;
			}
		}
		if (numeroTour == 2 && joueur == joueurInitial
				&& coupTemporaireDeuxiemeTour.getAbsJouee() == abs
				&& coupTemporaireDeuxiemeTour.getOrdJouee() == ord) {
			for (int j = ord - 1; j <= ord + 1; j = j + 2) {
				for (int i = abs - 1; i <= abs + 1; i = i + 2) {
					if (i >= 0 && i <= 14 && j >= 0 && j <= 14
							&& coupInitialPremierTour.getAbsJouee() == i
							&& coupInitialPremierTour.getOrdJouee() == j) {
						if (i <= 7 && j <= 7) {
							if (abs > i && ord > j) {
								res = 20;
							}
						}
						if (i > 7 && j <= 7) {
							if (abs < i && ord > j) {
								res = 20;
							}
						}
						if (i <= 7 && j > 7) {
							if (abs > i && ord < j) {
								res = 20;
							}
						}
						if (i > 7 && j > 7) {
							if (abs < i && ord < j) {
								res = 20;
							}
						}
					}
				}
			}
		}
		// FIXME fonction somme des 2^nbPionsAlignes discutable
		// mais bien pour faire des tas. Fonction somme des
		// nbPionsAlignes^3 potentiellement bien aussi, mais
		// mieux pour favoriser les lignes.
		res += (int) (Math.pow(detectionLigne(joueur, abs, ord), 3)
				+ Math.pow(detectionColonne(joueur, abs, ord), 3)
				+ Math.pow(detectionDiagVersBasDroite(joueur, abs, ord), 3) + Math
				.pow(detectionDiagVersHautDroite(joueur, abs, ord), 3));

		return res;
	}

	private int detectionDiagVersBasDroite(int joueur, int abs, int ord) {
		return detectionDirection(joueur, abs, ord, 1, 1);
	}

	private int detectionDiagVersHautDroite(int joueur, int abs, int ord) {
		return detectionDirection(joueur, abs, ord, 1, -1);
	}

	private int detectionColonne(int joueur, int abs, int ord) {
		return detectionDirection(joueur, abs, ord, 0, 1);
	}

	private int detectionLigne(int joueur, int abs, int ord) {
		return detectionDirection(joueur, abs, ord, 1, 0);
	}

	private int detectionDirection(int joueur, int abs, int ord,
			int controleDecalAbs, int controleDecalOrd) {
		int espaces = 0;
		int limite = 2;
		int decalage = 0;
		int compteurPts = 0;
		boolean limiteNonAtt;

		limiteNonAtt = true;
		decalage = 0;
		compteurPts = 0;

		while (limiteNonAtt) {
			if ((plateau[abs + controleDecalAbs * decalage][ord
					+ controleDecalOrd * decalage]) == joueur) {
				compteurPts++;
			}
			if ((plateau[abs + controleDecalAbs * decalage][ord
					+ controleDecalOrd * decalage]) == -joueur) {
				limiteNonAtt = false;
			} else {
				espaces++;
			}
			decalage++;
			if (abs + controleDecalAbs * decalage > 14
					|| ord + controleDecalOrd * decalage > 14
					|| ord + controleDecalOrd * decalage < 0
					|| decalage > limite) {
				limiteNonAtt = false;
			}
		}

		limiteNonAtt = true;
		decalage = 0;
		while (limiteNonAtt) {
			if ((plateau[abs - (controleDecalAbs * decalage)][ord
					- (controleDecalOrd * decalage)]) == joueur) {
				compteurPts++;
			}
			if ((plateau[abs - (controleDecalAbs * decalage)][ord
					- (controleDecalOrd * decalage)]) == -joueur) {
				limiteNonAtt = false;
			} else {
				espaces++;
			}
			decalage++;
			if (abs - (controleDecalAbs * decalage) < 0
					|| ord - (controleDecalOrd * decalage) > 14
					|| ord - (controleDecalOrd * decalage) < 0
					|| decalage > limite) {
				limiteNonAtt = false;
			}
		}

		compteurPts--;
		espaces--;

		if (espaces < 5) {
			compteurPts = 0;
		}
		if (compteurPts == 4) {
			compteurPts = 10; // exageration du score des alignements critiques
								// pour 4 pions
		}
		if (compteurPts == 5) {
			compteurPts = 20; // exageration du score des alignements critiques
								// pour 5 pions
		}
		return compteurPts;
	}

	/** fin des methodes d'heuristique **********************************************/

	public Coup getCoup() {
		return coupArrivant;
	}

	public IntervalleAlphaBeta getIntervalleAlphaBeta() {
		return iAB;
	}

	public Coup getNiemeCoupJouable(int n) {
		int i, j, cpt;
		cpt = 0;
		i = 0;
		j = 0;
		while (j < 15 && cpt < n) {
			i = 0;
			while (i < 15 && cpt < n) {
				if (plateau[i][j] == 0) {
					cpt++;
				}
				i++;
			}
			j++;
		}
		Coup res = null;
		if (cpt == n) {
			res = new Coup(i - 1, j - 1);
		}
		return res;
	}

	public static void setPlateau(int[][] plat) {
		plateau = plat;
	}

	public static void setProfMax(int pm) {
		profondeurMax = pm;
	}

	public static void setJoueurInit(int joueurCour) {
		joueurInitial = joueurCour;
	}

	public static Coup getCoupOptimal() {
		return coupOptimal;
	}

}
